// design structure
var canvas;
var context;
var canvasWidth;
var canvasHeight;
var message;
// game logic
var score;
var grid;
var gridWidth;
var gridHeight;
var player;
var spriteWidth;
var spriteHeight;
var isGameStarted;
var currentDirection;
var lifeSize;
var speed;
var reverse;
var borderLeft;
var borderTop;
var borderRight;
var borderBottom;
// constants
var DIRECTION;
var MS_PER_SECONDS;
// Variables -- TO DO -- REMOVE USELESS VARIABLES

window.addEventListener('load', function() {
    initData();
    initGraphics();
    drawMenu();
    document.onkeydown = menuKeyPress;
}, false); // runs on start ----------------------------

function initData() {
    MS_PER_SECONDS = 1000;
    isGameStarted = false;
    speed = 15;
    reverse = true;

    {
        DIRECTION = {
            LEFT: {},
            UP: {},
            RIGHT: {},
            DOWN: {}
        }; //creating direction enums -------------------------
        {
            DIRECTION.LEFT.OPPOSITE = DIRECTION.RIGHT;
            DIRECTION.UP.OPPOSITE = DIRECTION.DOWN;
            DIRECTION.RIGHT.OPPOSITE = DIRECTION.LEFT;
            DIRECTION.DOWN.OPPOSITE = DIRECTION.UP;
        } //Assigning opposite directions ---------------------------------
    } // DIRECTION enum ---------------------------------------------------

    gridWidth = 20;
    gridHeight = gridWidth;

} // --------------------------------------------------

function initGraphics() {
    {
        borderLeft = 5;
        borderRight = 5;
        borderBottom = 5;
        borderTop = 20;
        canvas = document.getElementById("snake_canvas");
        canvasWidth = 600;
        canvasHeight = canvasWidth;
        spriteWidth = canvasWidth / gridWidth;
        spriteHeight = canvasHeight / gridHeight;
        canvas.setAttribute("width", canvasWidth + borderLeft + borderRight);
        canvas.setAttribute("height", canvasHeight + borderTop + borderBottom);
    } // external ---------------------------------------------------------
    context = canvas.getContext("2d");
} // initData must run first ----------------------

function newGame() {
    score = 0;
    lifeSize = 1;
    player = new Player(gridWidth, gridHeight);
    currentDirection = null;

    grid = new Array();
    for (var i = 0; i < gridWidth; i++) {
        grid[i] = new Array();
        for (var j = 0; j < gridHeight; j++) {
            grid[i][j] = new Cell();
        }
    }
    grid[player.X][player.Y].isAlive = lifeSize;
    makeBonus();
    if (!isGameStarted) {
        isGameStarted = true;
        gameLoop();
    }
    document.onkeydown = gameKeyPress;
} // only runs on event --------------------------------

function makeBonus() {
    var bonusX;
    var bonusY;
    do {
        bonusX = Math.floor(Math.random() * (gridWidth));
        bonusY = Math.floor(Math.random() * (gridHeight));
    } while (grid[bonusX][bonusY].isAlive > 0 && !(player.X === bonusX && player.Y === bonusY));

    grid[bonusX][bonusY].isBonus = true;
} // -------------------------------------------------

function gameLoop() {
    if (isGameStarted) {
        update();
        draw();
        setTimeout(gameLoop, MS_PER_SECONDS / speed);
    }
} // game is running ----------------------------------

function update() {
    if (player.direction !== null) {
        for (var i = 0; i < gridWidth; i++) {
            for (j = 0; j < gridHeight; j++) {
                if (grid[i][j].isAlive > 0) {
                    grid[i][j].isAlive--;
                }
            }
        } // deplete all life by 1 ----

        var newX = player.X;
        var newY = player.Y;
        switch (player.direction) {
            case DIRECTION.LEFT:
                newX--;
                break;
            case DIRECTION.UP:
                newY--;
                break;
            case DIRECTION.RIGHT:
                newX++;
                break;
            case DIRECTION.DOWN:
                newY++;
                break;
        } // determine player direction ---------

        {
            if (newX < 0) {
                newX = gridWidth - 1;
            } else if (newX >= gridWidth) {
                newX = 0;
            }
            if (newY < 0) {
                newY = gridWidth - 1;
            } else if (newY >= gridWidth) {
                newY = 0;
            }
        } // go through the wall ------------------------------------------

        if (grid[newX][newY].isAlive <= 0) {
            player.X = newX;
            player.Y = newY;
            message = "Score: " + score;

            grid[player.X][player.Y].isAlive = lifeSize; // make cell alive ----

            if (grid[player.X][player.Y].isBonus) {
                grid[player.X][player.Y].isBonus = false;
                score += Math.floor(lifeSize * speed / gridWidth) + 1;
                makeBonus();
                lifeSize++;
            } // ran over bonus -----

        } else {
            isGameStarted = false;
            message = "Game Over! Final Score: " + score;
        } // has crashed ---
    } else {
        message = "Press a direction to begin.";
    } // has game started ------
    currentDirection = player.direction; // for error conflict -----------------
} // TO DO -- REFACTOR THIS METHOD ----------------------

function draw() {
    drawBackground();
    drawSprites();
    drawText();
} // typical draw method ----------------------------------

function drawBackground() {
    context.clearRect(0, 0, canvasWidth + borderLeft + borderRight, canvasHeight + borderTop + borderBottom);
    context.fillStyle = "#333333";
    context.fillRect(0, 0, canvasWidth + borderLeft + borderRight, canvasHeight + borderTop + borderBottom);
    context.fillStyle = "#050005";
    context.fillRect(borderLeft, borderTop, canvasWidth, canvasHeight);
} // clear then draw blank square ---------------

function drawMenu() {
    if (!isGameStarted) {
        drawBackground();
        context.fillStyle = '#080';
        context.font = '20px arcade';
        context.textAlign = 'center';
        context.textBaseline = 'top';
        context.fillText("Press Enter", canvas.width / 2, canvas.height / 2);
        setTimeout(drawMenu, MS_PER_SECONDS / speed);
    }
}

function drawSprites() {
    context.strokeStyle = "#0f0";
    context.fillStyle = "#008800";
    var printValue = "Print: ";
    for (var i = 0; i < gridWidth; i++) {
        for (var j = 0; j < gridHeight; j++) {
            if (grid[i][j].isAlive > 0) {
                if (reverse) {
                    var greenValue = 200;
                    var x = (greenValue / lifeSize);
                    x = Math.floor(x * parseInt(grid[i][j].isAlive));
                    printValue += x;
                    printValue += " ";
                    context.fillStyle = "rgb(0," + x + ",0)";
                    context.fillRect(i * spriteWidth + borderLeft, j * spriteHeight + borderTop, spriteWidth, spriteHeight);
                    context.strokeRect(i * spriteWidth + borderLeft, j * spriteHeight + borderTop, spriteWidth, spriteHeight);
                } else {
                    context.clearRect(i * spriteWidth + borderLeft, j * spriteHeight + borderTop, spriteWidth, spriteHeight);
                    context.strokeRect(i * spriteWidth + borderLeft, j * spriteHeight + borderTop, spriteWidth, spriteHeight);
                }
            } else if (grid[i][j].isBonus === true) {
                if (!reverse) {
                    context.fillRect(i * spriteWidth + borderLeft, j * spriteHeight + borderTop, spriteWidth, spriteHeight);
                    context.strokeRect(i * spriteWidth + borderLeft, j * spriteHeight + borderTop, spriteWidth, spriteHeight);
                } else {
                    context.clearRect(i * spriteWidth + borderLeft, j * spriteHeight + borderTop, spriteWidth, spriteHeight);
                    context.strokeRect(i * spriteWidth + borderLeft, j * spriteHeight + borderTop, spriteWidth, spriteHeight);
                }
            }
        }
    }
} // TO DO -- REFACTOR THIS METHOD -----------------

function drawText() {
    context.fillStyle = '#080';
    context.font = '20px arcade';
    context.textAlign = 'left';
    context.textBaseline = 'top';
    context.fillText(message, 0, 0);
} // TO DO -- Possible redesign?? ---------------------

function menuKeyPress(e) {
    switch (e.keyCode) {
        case 13: //enter
            newGame()();
            break;
    }
}

function gameKeyPress(e) {
    switch (e.keyCode) {
        case 13: //enter
            newGame();
            break;
        case 37:
            changeDirection(DIRECTION.LEFT);
            break;
        case 38:
            changeDirection(DIRECTION.UP);
            break;
        case 39:
            changeDirection(DIRECTION.RIGHT);
            break;
        case 40:
            changeDirection(DIRECTION.DOWN);
            break;
    }
} // Key press event handler -----------------------

function changeDirection(direction) {
    if (currentDirection === null || (direction !== currentDirection.OPPOSITE && currentDirection !== direction)) {
        player.direction = direction;
    }
} // Prevents illogical movement ------

function Player(X, Y) {
    this.X = Math.floor(X / 2);
    this.Y = Math.floor(Y / 2);
    this.direction = null;
} // Prototype -- Player ----------------------------

function Cell() {
    this.isAlive = 0;
    this.isBonus = false;
} // Prototype -- Cell ------------------------------------
